﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using System.Windows.Forms;
using System.Threading;

namespace binary
{
    public class myDBF
    {
       
        public List<DbfColumn> Kolumny = new List<DbfColumn>();
        public List<ImportError> ListaBledow = new List<ImportError>();
        public delegate void delPamiec(DataTable Dane);
        public long PamiecStart; // liczba bajtów zajętych przez aplikację
        public long RozmiarPliku = 0; // rozmiar pliku DBF w bajtach
        public int LiczbaRekordow { get; set; } // liczba rekordów razem z usuniętymi
        public int LiczRekNieusunietych { get; set; } // liczba rekordów nieusuniętych
        public int Odczytano { get; set; } //liczba odczytanych rekordów
        public int DlugoscRekordu { get; set; } //długość jednego rekordu [bajty]
        public int TypBazy; // pole określa jaki typ bazy odczytujemy
        public Int16 PozycjaPierwszego { get; set; } // wskaźnik na pierwszy bajt danych
        public bool OdczytanoNaglowek { get; set; } // informuje czy odczytano juz dane z naglowka
        public bool CzyCzytac = true;
        public bool Koniec = false;
        public string Sciezka = ""; // scieżka do pliku
        public string Plik { get; set; } // nazwa pliku
        public string NazwaBazy { get; set; } // nazwa tabeli
        public DataTable DaneTest;
        public int LimitPamieci = 20; //limit pamięci


        /*
         * Konstruktor odczytuje wszystkie dane z nagłówka pliku
         */
        public myDBF(string plik, string nazwa_bazy)
        {
            this.OdczytanoNaglowek = false; // nie odczytano jescze naglowka
            this.Plik = plik;
            this.Sciezka = Path.GetDirectoryName(this.Plik);
            this.RozmiarPliku = new FileInfo(this.Plik).Length;
            this.NazwaBazy = nazwa_bazy;
            this.LiczRekNieusunietych = 0;
            using (BinaryReader b = new BinaryReader(File.Open(plik, FileMode.Open, FileAccess.Read)))
            {
                try
                {
                    byte[] tab = new byte[32];
                    byte znak_konca = 0;
                    
                    b.Read(tab, 0, 32);
                    this.TypBazy = tab[0];
                    this.LiczbaRekordow = BitConverter.ToInt32(tab, 4); //odczyt liczby rekordów
                    this.PozycjaPierwszego = BitConverter.ToInt16(tab, 8); //odczyt liczby rekordów
                    this.DlugoscRekordu = BitConverter.ToInt16(tab, 10); //odczyt liczby rekordów
                    while (znak_konca != 13)
                    {                   
                        b.Read(tab, 0, 32); //odczyt informacji o klumnie
                        DbfColumn kol = new DbfColumn();
                        kol.Name = Encoding.ASCII.GetString(bajty(tab, 0, 10)).Replace("\0",""); //zapisanie nazwy
                        kol.Type = Encoding.ASCII.GetChars(bajty(tab, 11, 1)); //typ pola
                        kol.Length = tab[16]; // dlugosc
                        kol.LengthDecimal = tab[17]; //decimal places
                        znak_konca = b.ReadByte();
                        b.BaseStream.Seek(-1, SeekOrigin.Current);
                        Kolumny.Add(kol);
                        kol = null; // pozbywamy się zmiennej
                    }
                    ustalDlugoscRekordu();
                    this.OdczytanoNaglowek = true;
                }
                catch(Exception ex)
                {
                    MessageBox.Show("MyDBF konstruktor: " +ex.Message);
                }

                /*
                 * Wykonujemy pętle w celu wyliczenia liczby nieusunietych rekordow
                 * usuniete rekordy maja oznaczenie 42
                 */
                b.BaseStream.Seek(this.PozycjaPierwszego, SeekOrigin.Begin);
                byte status = 0;
                while (b.BaseStream.Position < this.RozmiarPliku - 1)
                {
                    status = b.ReadByte();
                    if (status != 42) // rekord nieusunięty
                        LiczRekNieusunietych++;
                    b.BaseStream.Seek(this.DlugoscRekordu, SeekOrigin.Current);
                }
                b.Close();
            }
        }


        /*
         * Metoda sprawdza zużycie pamięci
         * co 0,1 sekundy sprawdzamy czy nie odczytalismy zbyt duzo danych
         */
        private void kontrolujPamiec()
        {
            try
            {
                while (Koniec == false)
                {
                    Thread.Sleep(300);
                    if (this.CzyCzytac)
                    {
                        if ((GC.GetTotalMemory(true)) / 1024 / 1024 > LimitPamieci)
                            this.CzyCzytac = false; // blokujemy czytanie danych
                        else
                            this.CzyCzytac = true; // czytamy dane z pliku
                    }
                    else
                    {
                        if (this.DaneTest.Rows.Count == 0)
                            this.CzyCzytac = true;
                    }
                }
            }
            catch (ThreadAbortException)
            {
                // tutaj nic nie robimy
            }
            catch (Exception ex)
            {
                MessageBox.Show("Błąd kontroli pamięci: " + ex.Message);
            }
        }



        private byte[] bajty(byte[] tablica, int index, int liczba)
        {
            byte[] tab = new byte[liczba];
            int licznik = 0;
            for (int i = index; i < index + liczba; i++)
            {
                tab[licznik] =  tablica[i];
                licznik++;
            }
            return tab;
        }


        private void ustalDlugoscRekordu()
        {
            int dlugosc = 0;
            foreach (DbfColumn kol in Kolumny)
            { dlugosc += kol.Length; }
            this.DlugoscRekordu = dlugosc;
        }


        public void pobierzDane(DataTable Dane)
        {
            Dane.Columns.Clear();
            Type typ;
            foreach (DbfColumn col in Kolumny)
            {
                if(col.Type[0]=='N' && col.LengthDecimal==0 && col.Length<10) typ = System.Type.GetType("System.Int32");
                if (col.Type[0] == 'N' && col.LengthDecimal == 0 && col.Length >= 10) typ = System.Type.GetType("System.Int64");
                else if(col.Type[0]=='N' && col.LengthDecimal>0) typ = System.Type.GetType("System.Decimal");
                else if (col.Type[0] == 'M') typ = System.Type.GetType("System.String");
                else if (col.Type[0] == 'D') typ = System.Type.GetType("System.String");
                else if (col.Type[0] == 'L' ) typ = System.Type.GetType("System.Boolean");
                else if (col.Type[0] == 'C') typ = System.Type.GetType("System.String");
                else if (col.Type[0] == 'T') typ = System.Type.GetType("System.String");
                else if (col.Type[0] == 'I') typ = System.Type.GetType("System.Int32");
                else typ = System.Type.GetType("System.String");
                Dane.Columns.Add(col.Name,typ);
                typ = null;
            }
            GC.Collect();

            string wartoscString = "";
            string dataString = "";
            DateTime d;

            this.DaneTest = Dane;

            /* T */
            byte[] dataByte = new byte[4];
            byte[] czasByte = new byte[4];
            int intData;
            int intCzas;
            /*----------*/
            PamiecStart = GC.GetTotalMemory(true) / 1024 / 1024;
            Thread t = new Thread(new ThreadStart(kontrolujPamiec));
            t.Start();
            using (BinaryReader b = new BinaryReader(File.Open(this.Plik, FileMode.Open, FileAccess.Read)))
            {
                long rozmiar = new FileInfo(this.Plik).Length;
                b.BaseStream.Seek(this.PozycjaPierwszego, SeekOrigin.Begin);
                int liniaMemo;
                string tekstMemo;
                string sLinia;
                DataRow dr;
                byte status;
                while (b.BaseStream.Position < rozmiar - 1)
                    {
                        if (CzyCzytac)
                        {
                            Monitor.Enter(Dane);
                            status = b.ReadByte();
                            dr = Dane.NewRow();
                            foreach (DbfColumn col in Kolumny)
                            {
                                if (col.Type[0].ToString() == "M")
                                {
                                    try
                                    {

                                        if (this.TypBazy == 48 || this.TypBazy == 49 || this.TypBazy == 50)
                                        {
                                            liniaMemo = b.ReadInt32();
                                            tekstMemo = DbfMemo.getText(this.Sciezka + @"\" + Path.GetFileNameWithoutExtension(this.Plik) + ".fpt", liniaMemo, 1250);
                                            dr[col.Name] = tekstMemo;
                                        }
                                        else
                                        {
                                            sLinia = Encoding.GetEncoding(852).GetString(b.ReadBytes(col.Length)).Replace(" ", "");
                                            if (sLinia.Length > 0)
                                            {
                                                liniaMemo = int.Parse(sLinia);
                                                tekstMemo = DbfMemo.getText(this.Sciezka + @"\" + this.NazwaBazy + ".fpt", liniaMemo, 852);
                                                dr[col.Name] = tekstMemo;
                                            }
                                            else
                                            {
                                                dr[col.Name] = DBNull.Value;
                                            }
                                        }
                                        tekstMemo = null;

                                    }
                                    catch (Exception ex)
                                    {
                                        ImportError err = new ImportError(this.NazwaBazy, col.Name, this.Odczytano + 1, "Błąd odczytu pola memo: " + ex.Message);
                                        this.ListaBledow.Add(err);
                                        dr[col.Name] = DBNull.Value;
                                    }
                                }
                                else if (col.Type[0] == 'D')
                                {
                                    
                                    try
                                    {
                                        wartoscString = Encoding.Default.GetString(b.ReadBytes(col.Length)).Trim();
                                        if (wartoscString.Length > 0)
                                        {
                                            if (wartoscString.Length == 8)
                                            {
                                                dataString = wartoscString.Insert(6, "-").Insert(4, "-");
                                                if (int.Parse(dataString.Substring(0, 4)) > 1800)
                                                    dr[col.Name] = dataString;
                                                else
                                                    throw new Exception(String.Format("Nieprawidłowy format daty: {0}", wartoscString));
                                                dataString = null;
                                            }
                                            else
                                                throw new Exception(String.Format("Nieprawidłowy format daty: {0}", wartoscString));
                                        }
                                        else
                                            dr[col.Name] = DBNull.Value;
                                    }
                                    catch (Exception ex)
                                    {
                                        dr[col.Name] = DBNull.Value;
                                        ImportError err = new ImportError(this.NazwaBazy, col.Name, this.Odczytano + 1, ex.Message);
                                        this.ListaBledow.Add(err);
                                    }
                                    wartoscString = null;

                                }
                                else if (col.Type[0] == 'N' && col.LengthDecimal == 0)
                                {
                                    try
                                    {
                                        wartoscString = Encoding.Default.GetString(b.ReadBytes(col.Length)).Trim();
                                        if (wartoscString.Length > 0 && wartoscString.Length < 10) 
                                            dr[col.Name] = int.Parse(wartoscString);
                                        else if (wartoscString.Length >= 10) 
                                            dr[col.Name] = Int64.Parse(wartoscString);
                                        else dr[col.Name] = DBNull.Value;
                                    }
                                    catch (Exception ex)
                                    {
                                        ImportError err = new ImportError(this.NazwaBazy, col.Name, this.Odczytano + 1, String.Format("{0}, Wartość: {1}",ex.Message,wartoscString));
                                        this.ListaBledow.Add(err);
                                        dr[col.Name] = DBNull.Value;
                                    }
                                    wartoscString = null;
                                }
                                else if (col.Type[0] == 'N' && col.LengthDecimal > 0)
                                {
                                    try
                                    {
                                        wartoscString = Encoding.Default.GetString(b.ReadBytes(col.Length)).Trim().Replace(".", ",");
                                        if (wartoscString.Length > 0)
                                            dr[col.Name] = decimal.Parse(wartoscString);
                                        else dr[col.Name] = DBNull.Value;
                                        wartoscString = null;
                                    }
                                    catch (Exception ex)
                                    {
                                        ImportError err = new ImportError(this.NazwaBazy, col.Name, this.Odczytano + 1, String.Format("{0}, Wartość: {1}", ex.Message, wartoscString));
                                        this.ListaBledow.Add(err);
                                        dr[col.Name] = DBNull.Value;
                                    }
                                }
                                else if (col.Type[0] == 'L')
                                {
                                    try
                                    {
                                        wartoscString = Encoding.Default.GetString(b.ReadBytes(col.Length)).Trim();
                                        if (wartoscString == "T") dr[col.Name] = true;
                                        else if (wartoscString.Length == 0) dr[col.Name] = DBNull.Value;
                                        else dr[col.Name] = false;
                                        wartoscString = null;
                                    }
                                    catch (Exception ex)
                                    {
                                        ImportError err = new ImportError(this.NazwaBazy, col.Name, this.Odczytano + 1, ex.Message);
                                        this.ListaBledow.Add(err);
                                        dr[col.Name] = DBNull.Value;
                                    }
                                }
                                else if (col.Type[0] == 'T')
                                {
                                    try
                                    {
                                        dataByte = new byte[4];
                                        czasByte = new byte[4];

                                        dataByte = b.ReadBytes(4);
                                        czasByte = b.ReadBytes(4);

                                        intData = BitConverter.ToInt32(dataByte, 0);
                                        intCzas = BitConverter.ToInt32(czasByte, 0);

                                        d = new DateTime(1900, 1, 1);
                                        d = d.AddDays(intData - 2415021);
                                        d = d.AddMilliseconds(intCzas);

                                        dr[col.Name] = d;
                                        dataByte = null;
                                        czasByte = null;

                                    }
                                    catch (Exception ex)
                                    {
                                        ImportError err = new ImportError(this.NazwaBazy, col.Name, this.Odczytano + 1, ex.Message);
                                        this.ListaBledow.Add(err);
                                        dr[col.Name] = DBNull.Value;
                                    }
                                }
                                else if (col.Type[0] == 'I')
                                {
                                    try
                                    {
                                        dr[col.Name] = b.ReadInt32();
                                    }
                                    catch (Exception ex)
                                    {
                                        ImportError err = new ImportError(this.NazwaBazy, col.Name, this.Odczytano + 1, ex.Message);
                                        this.ListaBledow.Add(err);
                                        dr[col.Name] = DBNull.Value;
                                    }
                                }
                                else
                                {
                                    try
                                    {
                                        wartoscString = Encoding.GetEncoding(1250).GetString(b.ReadBytes(col.Length));
                                        if (col.Name.Equals("_NullFlags"))
                                            wartoscString = string.Empty;
                                        
                                        
                                        if (wartoscString.Trim().Length > 0)
                                            dr[col.Name] = wartoscString.Trim();
                                        else
                                            dr[col.Name] = DBNull.Value;

                                        wartoscString = null;
                                    }
                                    catch (Exception ex)
                                    {
                                        ImportError err = new ImportError(this.NazwaBazy, col.Name, this.Odczytano + 1, ex.Message);
                                        this.ListaBledow.Add(err);
                                        dr[col.Name] = DBNull.Value;
                                    }
                                }
                            }
                            //if (status != 32) 

                            if (status != 42) // rekord nieusunięty
                            {
                                this.Odczytano++;
                                Dane.Rows.Add(dr);
                            }
                            //dr = null;   
                            Monitor.Exit(Dane);
                        }
                        else
                        {
                            Thread.Sleep(100);
                        }
                        //if (this.Odczytano == 100)
                        //    break;
                    }
                GC.Collect();
                b.Close();
                Koniec = true;
            }
            t.Abort();
            t.Join();
        }


    }
}
